/*
 * Copyright 2008 - 2009 Apple, Inc.
 * Copyright 2009 - 2010 Plausible Labs Cooperative, Inc.
 *
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#import "NSBlock.h"

#import <objc/objc-auto.h>
#import <objc/runtime.h>

/**
 * @ingroup private_api
 * @{
 */

#define BLOCK(id) ((struct Block_layout *)id)

// Weak import Apple's stack and global block classes.
extern void *_NSConcreteStackBlock __attribute__((weak_import));
extern void *_NSConcreteGlobalBlock __attribute__((weak_import));

/**
 * Abstract Block Superclass.
 */
@implementation PLBlock

- (id) copyWithZone: (NSZone *) zone {
#ifdef __OBJC_GC__
    /* If garbage collection is enabled, make a collectable copy. */
    if (objc_collectingEnabled())
        return _PLBlock_copy_gc((void *) self);
    else
        return _PLBlock_copy((void *) self);
#else
    return _PLBlock_copy((void *) self);
#endif /* __OBJC_GC__ */
}

- (id) copy {
    return [self copyWithZone: nil];
}

- (void) dealloc {
    /* Quiesce [super dealloc] warnings */
    if (NO) [super dealloc];
}

@end


/**
 * A malloc-allocated block.
 */
@implementation _PLMallocBlock


- (id) retain {
    return [self copyWithZone: nil];
}

- (void) release {
    PLBlock_release((void *) self);
}

- (NSUInteger) retainCount {
    return BLOCK(self)->flags & BLOCK_REFCOUNT_MASK;
}

@end


/**
 * A stack allocated block.
 */
@implementation _PLStackBlock

// Force early class initialization to ensure that this class is valid when
// referenced by a block
+ (void) load {}

/* Update self->isa to point at the Apple runtime */
- (void) pl_classFixup {
    object_setClass(self, (Class) &_NSConcreteStackBlock);
}

/* Forward unknown methods implemented by Apple's block classes. */
- (id) forwardingTargetForSelector: (SEL) aSelector {
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        return self;
    }

    return nil;
}

- (id) copyWithZone: (NSZone *) zone {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        return [self copyWithZone: zone];
    }

    return [super copyWithZone: zone];
}

- (id) copy {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        return [self copy];
    }

    return [super copy];
}

- (id) retain {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        return [self retain];
    }

    /* Allocated on stack */
    return self;
}

- (void) release {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        [self release];
        return;
    }

    /* Allocated on stack */
}

- (id) autorelease {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        return [self autorelease];
    }

    /* Allocated on stack */
    return self;
}

- (NSUInteger) retainCount {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        return [self retainCount];
    }

    return 1;
}

- (void) dealloc {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        [self dealloc];
        return;
    }

    [super dealloc];
}

@end

/**
 * A global block.
 */
@implementation _PLGlobalBlock

// Force early class initialization to ensure that this class is valid when
// referenced by a block
+ (void) load {}

/* Update self->isa to point at the Apple runtime */
- (void) pl_classFixup {
    object_setClass(self, (Class) &_NSConcreteGlobalBlock);
}

/* Forward unknown methods implemented by Apple's block classes. */
- (id) forwardingTargetForSelector: (SEL) aSelector {
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        return self;
    }
    
    return nil;
}

- (id) copyWithZone: (NSZone *) zone {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        return [self copyWithZone: zone];
    }

    return self; 
}

- (id) copy {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        return [self copy];
    }

    return self;
}

- (id) retain {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        return [self retain];
    }

    return self;
}

- (void) release {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        [self release];
        return;
    }
}

- (id) autorelease {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        return [self autorelease];
    }
    
    /* Allocated on stack */
    return self;
}

- (NSUInteger) retainCount {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        return [self retainCount];
    }
    
    return 1;
}

- (void) dealloc {
    /* Delegate to Apple runtime */
    if (PLBlock_AppleRuntimeEnabled) {
        [self pl_classFixup];
        [self dealloc];
        return;
    }
    
    [super dealloc];
}

@end

/**
 * A GC-collectable block.
 */
@implementation _PLAutoBlock

- (id) copyWithZone: (NSZone *) zone {
    return self; 
}

- (id) copy { 
    return self;
}

- (id) retain { 
    return self; 
}

- (void) release {
}

- (NSUInteger) retainCount { 
    return UINT_MAX; 
}

@end

/**
 * A GC-collectable block that will call the block's finalizer.
 */
@implementation _PLFinalizingBlock

- (void) finalize {
    /* Call the block dispose handler */
    struct Block_layout *aBlock = (struct Block_layout *) self;
    if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)
        (*aBlock->descriptor->dispose)(aBlock);

    [super finalize];
}

@end

/**
 * Provides an object-equivalent to Block_byref structure, with a __weak reference instance variable for the byref
 * value. This provides support for __block __weak value references.
 */
@implementation _PLWeakBlockVariable
@end

/**
 * @}
 */